package org.amuthu.expression;

/**
 * The abstract base class for all expressions in the <i>Gondola</i> language. This
 * class implements the {@link Expression} interface and provides functions for
 * managing an expression's child nodes. E.g. the expression <code>4+5</code> would
 * consist of three nodes. The plus being the root node, which would have two
 * child nodes of <code>4</code> and <code>5</code>.
 *
 * @see Expression
 */
public abstract class AbstractExpression implements Expression
{
   // Array of children.
   protected Expression children[];

   /**
    * Create a new expression with no children.
    *
    */
   //Terminal Expression
   public AbstractExpression()
   {
      children = null;
   }

   /**
    * Create a new expression with the given number of children.
    *
    * @param children An array of the children of the expression.
    */
   public AbstractExpression(Expression[] children)
   {
      this.children = children;
   }

   /**
    * Return the child of this node at the given index.
    *
    * @return child at given index.
    */
   @Override
   public Expression getChild(int index)
   {
      assert index <= getChildCount();

      return children[index];
   }

   /**
    * Return true if none of the children of this expression are null.
    * This means the expression can be simplified safely.
    *
    * @return true if all children are not null
    */
   @Override
   public boolean validTree()
   {
      boolean rv = true;

      for (int i = 0; i < getChildCount(); i++)
      {
         if (getChild(i) == null)
         {
            return false;
         }
         else
         {
            rv = getChild(i).validTree();
            if (rv == false)
            {
               return false;
            }
         }
      }
      return rv;
   }

   /**
    * Return the index of the given argument in the expression. This
    * method uses reference equality rather than using the equals
    * method.
    *
    * @param child the child expression to locate
    * @return index of the child expression or <code>-1</code> if it could
    *              not be found
    */
   @Override
   public int getIndex(Expression child)
   {
      for (int index = 0; index < getChildCount(); index++)
      {
         if (getChild(index) == child)
         {
            return index;
         }
      }

      // Not found
      return -1;
   }

   /**
    * Count the number of nodes in the tree.
    *
    * @return number of nodes or 1 if this is a terminal node
    */
   @Override
   public int size()
   {
      int count = 1;

      for (int i = 0; i < getChildCount(); i++)
      {
         Expression child = getChild(i);

         count += child.size();
      }

      return count;
   }

   /**
    * Count the number of nodes in the tree with the given type.
    *
    * @return number of nodes in the tree with the given type.
    */
   @Override
   public int size(int type)
   {
      int count = 0;

      assert (type == BOOLEAN_TYPE || type == FLOAT_TYPE || type == INTEGER_TYPE
              || type == FLOAT_QUOTE_TYPE || type == INTEGER_QUOTE_TYPE
              || type == STRING_TYPE);

      if (getType() == type)
      {
         count = 1;
      }

      for (int i = 0; i < getChildCount(); i++)
      {
         Expression child = getChild(i);

         count += child.size(type);
      }

      return count;
   }

   @Override
   public String toString()
   {
      String rv = "";
      for (int i = 0; i < getChildCount(); i++)
      {
         if (getChild(i) != null)
         {
            rv += getChild(i).toString();
         }
      }
      return rv;
   }

   /**
    * Debugging function which prints out an expresison tree
    * in a string.
    *
    * @return A string which represents the expression tree
    */
   @Override
   public String printTree(int level, int code)
   {
      String rv = "";

      for (int i = 0; i < level; i++)
      {
         rv += " ";
      }
      String subTree = "\n";
      for (int i = 0; i < getChildCount(); i++)
      {
         if (getChild(i) != null)
         {
            subTree += " " + getChild(i).printTree(level + 1, code) + " ";
         }
      }
      rv += subTree;
      return rv;
   }

}
